package edu.polsl.bio.str.manager.util;

import edu.polsl.bio.str.common.util.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.remoting.rmi.RmiRegistryFactoryBean;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.util.Collections;
import java.util.Enumeration;

/**
 * //todo class description
 * <p/>
 * Created on: Jun 14, 2011 10:12:16 AM <br/>
 * <a href="http://www.polsl.pl">www.polsl.pl</a>
 *
 * @author lukasz.olczak@polsl.pl
 */
public class RmiRegistryFactoryBeanExt extends RmiRegistryFactoryBean {
  /**
   * slf4j Logger.
   */
  private static final Logger log = LoggerFactory.getLogger(RmiRegistryFactoryBeanExt.class.getName());

  protected Registry getRegistry(String registryHost, int registryPort, RMIClientSocketFactory clientSocketFactory,
                                 RMIServerSocketFactory serverSocketFactory) throws RemoteException {

    String registryRemoteHost = registryHost;
    if (registryHost.trim().equals("0.0.0.0")) {
      try {
        registryRemoteHost = SystemUtils.getNonLoopbackAddress();
      } catch (UnknownHostException e) {
        log.error("Exception occurred during network address detection", e);
      }
    }
    try {
      return super.getRegistry(registryRemoteHost, registryPort, clientSocketFactory,
                               serverSocketFactory);
    } catch (RemoteException e) {
      try {
        InetAddress host = InetAddress.getByName(registryRemoteHost);
        if (isLocalAddress(host)) {
          log.debug("Not found rmi registry under " + host);
          log.debug("Creating one on localhost");
          setAlwaysCreate(true);
          return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
        } else {
          throw new RemoteException("", e);
        }
      } catch (UnknownHostException uhe) {
        throw new RemoteException("Unknown host " + registryRemoteHost, e);
      } catch (SocketException se) {
        throw new RemoteException("Socket exception occurred during checking address " + registryRemoteHost, e);
      }
    }
  }

  private static boolean isLocalAddress(InetAddress host) throws SocketException {
    Enumeration<NetworkInterface> nets = null;

    nets = NetworkInterface.getNetworkInterfaces();
    for (NetworkInterface netint : Collections.list(nets)) {
      Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();

      for (InetAddress inetAddress : Collections.list(inetAddresses)) {
        if (inetAddress.equals(host)) return true;
      }
    }

    return false;
  }
}
